﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Linq;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
using System.IO;
using StorageManager.Properties;
using System.Data.Services.Client;
using Astoman.Common.Repository;
using Astoman.Common;

namespace StorageManager
{
    public enum BlobActionScope { None, Blob, Container };

    public partial class MainWindow : Form
    {
        private BlobPropertyControl blobPropControl = null;
        private ContainerPropertyControl containerPropControl = null;
        private BlobActionScope CurrentBlobActionScope = BlobActionScope.None;

        private TableRepository _repTable = null;
        private QueueRepository _repQueue = null;
        private BlobRepository _repBlob = null;

        private const String FormTitle = "Azure Storage Manager";

        public MainWindow()
        {
            InitializeComponent();

            //Reuse settings from previous build
            Settings.Default.Upgrade();

            //Tell AzureStorageAccount which path to use to load/save settings
            AzureStorageAccount.SavePath = Settings.Default.AccountPath;

            if (AccountManager.CurrentAccount == null)
            {
                //If we cannot find any settings (see AccountManager.CurrentAccount for details)
                MessageBox.Show("No settings have been created yet, please enter information for at least one storage account");
                ShowAccountManager();
            }
            else
            {
                Log(String.Format("Using Account {0}", AccountManager.CurrentAccount.SettingName));
                SetTitle(AccountManager.CurrentAccount.SettingName);
                RefreshContent();
            }
            lblPath.Text = Settings.Default.LastSavePath;
        }

        #region Event Handlers

        private void btnDeleteTables_Click(object sender, EventArgs e)
        {
            var selectedNames = getSelectedCheckBoxes(flowPanelTable);
            if (selectedNames != null && selectedNames.Count > 0)
            {
                foreach (var tableName in selectedNames)
                {
                    _repTable.Delete(tableName);
                    Log(String.Format("Deleting Table {0}", tableName));
                }
            }
            CreateTableList();
        }

        private void btnSetLocation_Click(object sender, EventArgs e)
        {
            var result = folderBrowserDialog1.ShowDialog();
            btnCopy.Enabled = false;
            if (result == DialogResult.OK)
            {
                lblPath.Text = folderBrowserDialog1.SelectedPath;
            }
            if (Directory.Exists(lblPath.Text))
            {
                btnCopy.Enabled = true;
                Settings.Default.LastSavePath = lblPath.Text;
                Settings.Default.Save();
            }
        }

        private void btnCopy_Click(object sender, EventArgs e)
        {
            using (new HourGlass())
            {
                if (CurrentBlobActionScope == BlobActionScope.Container)
                {
                    var selected = listBoxContainers.SelectedItems.Cast<String>().ToList();
                    if (selected.Count > 0)
                    {
                        foreach (var containerName in selected)
                        {
                            Log(String.Format("Starting to save container {0} to disk path {1}", containerName, lblPath.Text));
                            bool didAnything = _repBlob.SaveContainerToDisk(containerName, lblPath.Text);
                            if (didAnything)
                                Log(String.Format("Finished saving container {0}", containerName));
                            else
                                Log(String.Format("Skipped empty container {0}", containerName));
                        }
                    }
                    else
                    {
                        MessageBox.Show("No container has been selected.");
                    }
                }
                else if (CurrentBlobActionScope == BlobActionScope.Blob)
                {
                    var selectedBlobs = listBoxBlobs.SelectedItems.Cast<String>().ToList();
                    var selectedContainers = listBoxContainers.SelectedItems.Cast<String>().ToList();

                    if (selectedBlobs.Count < 1)
                    {
                        MessageBox.Show("No blob has been selected.");
                        return;
                    }
                    if (selectedContainers.Count != 1)
                    {
                        MessageBox.Show("Please select exactly one container.");
                        return;
                    }

                    foreach (var blobName in selectedBlobs)
                    {
                        _repBlob.SaveBlobToDisk(selectedContainers[0], blobName, lblPath.Text);
                        Log(String.Format("Saved Blob {0} to disk path {1}", blobName, lblPath.Text));
                    }
                }
            }
        }

        private void manageAccountsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowAccountManager();
        }

        void settingsManager_SettingsChanged(AzureStorageAccount setting)
        {
            Settings.Default.LastAccount = setting.SettingName;
            Settings.Default.Save();
            SetTitle(setting.SettingName);
            RefreshContent();
        }

        private void listBoxContainers_SelectedIndexChanged(object sender, EventArgs e)
        {
            var selection = listBoxContainers.SelectedItems;
            BlobActionScopeChanged(BlobActionScope.Container);

            //If more than one or no container is selected, showing the contained blobs does not make sense
            if (selection.Count != 1)
            {
                listBoxBlobs.Items.Clear();
                if (containerPropControl != null) containerPropControl.ClearProperties();
            }
            else
            {
                using (new HourGlass())
                {

                    var blobProperties = _repBlob.GetBlobPropertiesInContainer(selection[0] as String);
                    var blobNames = (from prop in blobProperties
                                     select prop.Name).ToList();

                    FillBlobList(blobNames);

                    /*Calculate total size of all blobs in container*/
                    var totalSize = (from prop in blobProperties
                                     select prop.ContentLength).Sum();

                    if (blobPropControl != null) blobPropControl.ClearProperties();
                    BlobActionScopeChanged(BlobActionScope.Container);

                    ShowProperties(totalSize, blobNames.Count);
                }
            }

        }

        private void listBoxBlobs_SelectedIndexChanged(object sender, EventArgs e)
        {
            var selection = listBoxBlobs.SelectedItems;
            BlobActionScopeChanged(BlobActionScope.Blob);

            //If more than one or no blob is selected, showing the properties does not make sense
            if (selection.Count != 1)
            {
                if (blobPropControl != null) blobPropControl.ClearProperties();
            }
            else
            {
                using (new HourGlass())
                {
                    var prop = _repBlob.GetBlobProperties(listBoxContainers.SelectedItem as String, selection[0] as String);
                    ShowProperties(prop.ContentLength, null);
                }
            }
        }

        private void listBoxQueues_SelectedIndexChanged(object sender, EventArgs e)
        {
            var selection = listBoxQueues.SelectedItems;
            //If more than one or no queue is selected, showing the contained blobs does not make sense
            if (selection.Count != 1)
            {
                listBoxMessages.Items.Clear();
                //if (containerPropControl != null) containerPropControl.ClearProperties();
            }
            else
            {
                using (new HourGlass())
                {

                    var queueProperty = _repQueue.GetQueueProperties(selection[0] as String);
                    var messageProperties = _repQueue.GetMessagesInQueue(selection[0] as String);
                    var messages = (from message in messageProperties
                                    select message.Id).ToList();

                    FillMessageList(messages);

                    /*Calculate total size of all messages in queue*/
                    var totalSize = (from prop in messageProperties
                                     select prop.ContentAsBytes().Length).Sum();

                    //if (blobPropControl != null) blobPropControl.ClearProperties();
                    //BlobActionScopeChanged(BlobActionScope.Container);

                    //ShowProperties(totalSize, messages.Count);
                }
            }
        }

        private void refreshToolStripMenuItem_Click(object sender, EventArgs e)
        {
            RefreshContent();
        }

        private void btnDeleteBlob_Click(object sender, EventArgs e)
        {
            if (CurrentBlobActionScope == BlobActionScope.Blob)
            {
                var selectedBlobs = listBoxBlobs.SelectedItems.Cast<String>().ToList();
                var selectedContainers = listBoxContainers.SelectedItems.Cast<String>().ToList();

                if (selectedBlobs.Count < 1)
                {
                    MessageBox.Show("No blob has been selected.");
                    return;
                }
                if (selectedContainers.Count != 1)
                {
                    MessageBox.Show("Please select exactly one container.");
                    return;
                }

                var result = MessageBox.Show(this,
                                            String.Format("Do you really want to delete the selected {0} {1}?", selectedBlobs.Count, selectedBlobs.Count > 1 ? "blobs" : "blob"),
                                            "Confirmation",
                                            MessageBoxButtons.YesNo
                                            );

                if (result == DialogResult.Yes)
                {
                    foreach (var blobName in selectedBlobs)
                    {
                        _repBlob.DeleteBlob(selectedContainers[0], blobName);
                    }
                    CreateBlobList(selectedContainers[0]);
                }
            }
            else if (CurrentBlobActionScope == BlobActionScope.Container)
            {
                var selectedContainers = listBoxContainers.SelectedItems.Cast<String>().ToList();
                if (selectedContainers.Count < 1)
                {
                    MessageBox.Show("No container has been selected.");
                    return;
                }

                var result = MessageBox.Show(this,
                                            String.Format("Do you really want to delete the selected {0} {1}?", selectedContainers.Count, selectedContainers.Count > 1 ? "containers" : "container"),
                                            "Confirmation",
                                            MessageBoxButtons.YesNo
                                            );

                if (result == DialogResult.Yes)
                {
                    foreach (var containerName in selectedContainers)
                    {
                        _repBlob.DeleteContainer(containerName);
                    }
                    CreateContainerList();
                    listBoxBlobs.Items.Clear();
                }
            }
        }
        #endregion

        #region List content

        public void CreateTableList()
        {
            List<String> tables = _repTable.List();
            FillTableList(tables);
        }

        public void CreateContainerList()
        {
            List<String> containers = _repBlob.GetContainerNames();
            FillContainerList(containers);
        }

        public void CreateQueueList()
        {
            List<String> queues = _repQueue.List();
            FillQueueList(queues);
        }

        public void FillTableList(List<String> tables)
        {
            Log("Listing Tables");
            flowPanelTable.Controls.Clear();
            List<CheckBox> checkBoxList = new List<CheckBox>();
            foreach (var table in tables)
            {
                CheckBox box = new CheckBox();
                box.AutoSize = true;
                box.Text = table;
                box.Checked = false;
                checkBoxList.Add(box);
            }
            flowPanelTable.Controls.AddRange(checkBoxList.ToArray());
        }

        public void FillContainerList(List<String> containers)
        {
            Log("Listing Blob Containers");
            listBoxContainers.Items.Clear();
            listBoxContainers.Items.AddRange(containers.ToArray());
        }

        public void FillQueueList(List<String> queues)
        {
            Log("Listing Queues");
            listBoxQueues.Items.Clear();
            listBoxQueues.Items.AddRange(queues.ToArray());
        }

        private void CreateBlobList(String containerName)
        {
            var blobProperties = _repBlob.GetBlobPropertiesInContainer(containerName);
            var blobNames = (from prop in blobProperties
                             select prop.Name).ToList();

            FillBlobList(blobNames);
        }

        private void FillBlobList(List<String> items)
        {
            Log("Listing Blobs for selected container");
            listBoxBlobs.Items.Clear();
            listBoxBlobs.Items.AddRange(items.ToArray());
        }

        private void FillMessageList(List<String> items)
        {
            Log("Listing messages for selected queue");
            listBoxMessages.Items.Clear();
            listBoxMessages.Items.AddRange(items.ToArray());
        }

        #endregion

        /// <summary>
        /// BlobActionScope tells the program whether an action (delete, copy)
        /// works on blob level or on blob container level. This obviously changes 
        /// the way these actions work but also button text and states.
        /// All these changes should happen within this method, not directly in event handlers
        /// that trigger the changes.
        /// </summary>
        /// <param name="action"></param>
        void BlobActionScopeChanged(BlobActionScope action)
        {
            CurrentBlobActionScope = action;
            bool isCopyPathSet = lblPath.Text.Trim().Length > 0;

            if (action == BlobActionScope.None)
            {
                btnCopy.Text = "Copy";
                btnCopy.Enabled = false;

                btnDeleteBlob.Text = "Delete";
                btnDeleteBlob.Enabled = false;
            }
            else if (action == BlobActionScope.Container)
            {
                btnCopy.Text = "Copy Container";
                btnCopy.Enabled = isCopyPathSet;

                btnDeleteBlob.Text = "Delete Container";
                btnDeleteBlob.Enabled = true;
            }
            else if (action == BlobActionScope.Blob)
            {
                btnCopy.Text = "Copy Blob";
                btnCopy.Enabled = isCopyPathSet;

                btnDeleteBlob.Text = "Delete Blob";
                btnDeleteBlob.Enabled = true;
            }
        }

        /// <summary>
        /// Set the main window's title
        /// </summary>
        /// <param name="accountName"></param>
        private void SetTitle(String accountName)
        {
            this.Text = String.Format("{0} [Account: {1}]", FormTitle, accountName);
        }

        /// <summary>
        /// Encapsulates all the tasks that need to be performed whenever another setting is used
        /// and also on application startup. It essentially recreates all repositories and reloads all data.
        /// </summary>
        private void RefreshContent()
        {
            using (new HourGlass())
            {
                try
                {
                    _repBlob = new BlobRepository(AccountManager.CurrentAccount.BlobStorageAccountInfo);
                    _repQueue = new QueueRepository(AccountManager.CurrentAccount.QueueStorageAccountInfo);
                    _repTable = new TableRepository(AccountManager.CurrentAccount.TableStorageAccountInfo);

                    labelTableCount.Text = String.Format("{0} Tables", _repTable.Count());
                    labelQueueCount.Text = String.Format("{0} Queues", _repQueue.Count());
                    labelBlobCount.Text = String.Format("{0} Blobs in {1} containers", _repBlob.CountAllBlobs(), _repBlob.CountContainers());

                    CreateTableList();
                    CreateContainerList();
                    CreateQueueList();
                    listBoxBlobs.Items.Clear();
                    grpBoxProperties.Controls.Clear();
                }
                catch (DataServiceQueryException)
                {
                    MessageBox.Show(String.Format("The settings of account {0} seem to be invalid, cannot connect to service.", AccountManager.CurrentAccount.AccountName));
                    ShowAccountManager();
                }
            }
        }

        private List<String> getSelectedCheckBoxes(FlowLayoutPanel targetPanel)
        {
            List<String> selectedList = new List<String>();
            foreach (var control in targetPanel.Controls)
            {
                CheckBox target;
                if (control is CheckBox && (target = control as CheckBox).Checked == true)
                {
                    selectedList.Add(target.Text);
                }
            }
            return selectedList;
        }

        public void Log(String text)
        {
            String logText = String.Format("[{0}] {1}", DateTime.Now.ToLongTimeString(), text);
            listBoxLog.Items.Insert(0, logText);
            listBoxLog.Refresh();
        }

        private void ShowAccountManager()
        {
            AccountManager settingsManager = new AccountManager();
            //Subscribe to this event so we get notified when another setting is chosen
            settingsManager.SettingChosen += new SettingChosen(settingsManager_SettingsChanged);
            settingsManager.Show(this);
        }

        private void ShowProperties(long size, int? blobCount)
        {
            grpBoxProperties.Controls.Clear();
            if (blobCount == null)
            {
                blobPropControl = new BlobPropertyControl();
                blobPropControl.ShowProperties(size);
                grpBoxProperties.Controls.Add(blobPropControl);
                blobPropControl.Show();
            }
            else
            {
                containerPropControl = new ContainerPropertyControl();
                containerPropControl.ShowProperties(size, blobCount.Value);
                grpBoxProperties.Controls.Add(containerPropControl);
                containerPropControl.Show();
            }
        }

    }
}
